package com.ztspeech.vad;

import android.os.Handler;
import android.os.Message;

import org.dragonnova.meetingserver.utils.LogUtil;

import java.util.concurrent.ConcurrentLinkedQueue;

public class VadCheck {
    private String TAG = VadCheck.class.getName();
    private VoiceDetector vad;
    private boolean vadEnabled = true;
    private Handler mainHandler;
    public static final int VAD_START = 103;
    public static final int VAD_STOP = 104;
    public static final int VAD_NOVOICE = 105;
    public static final int VAD_DATA = 107;
    private long framNum = 0;
    private int MAXMUTETIME = 6 * 1000;
    private int vadVoiceFrameNum = 0;
    private boolean isWorking;
    private ConcurrentLinkedQueue<byte[]> waveQueue = new ConcurrentLinkedQueue<byte[]>();
    private boolean endQueue;
    private boolean vadStart = false;

    public VadCheck(Handler handler) {
        mainHandler = handler;
        vad = new VoiceDetector(0);
        vad.setParam(VadJni.VD_PARAM_PREFRAMENUM, 20);
        vad.setParam(VadJni.VD_PARAM_MINVOCFRAMENUM, 30);
        vad.setParam(VadJni.VD_PARAM_MINVOCRATIO, 0.6f);
        vad.setParam(VadJni.VD_PARAM_MINSILFRAMENUM, 70);
        vad.setParam(VadJni.VD_PARAM_MINSILRATIO, 0.9f);
        vad.setParam(VadJni.VD_PARAM_MINSPEECHFRAMENUM, 100);

        // vad.setParam(VadJni.VD_PARAM_MINENERGY, 400);
        // vad.setParam(VadJni.VD_PARAM_MAXENERGY, 5000);
    }

    public void inputVoice(byte[] voiceData, int offset, int length,
                           boolean isEnd) {
        if (!isWorking)
            return;

        if (offset < 0)
            offset = 0;
        if (length < 0)
            length = 0;
        framNum += length;
        if (voiceData != null && length > 0) {
            byte[] queueData = new byte[length];
            System.arraycopy(voiceData, offset, queueData, 0, length);
            waveQueue.add(queueData);
        }
        if (isEnd)
            endQueue = true;
    }

    private void setPostData(byte[] voiceData, int offset, int length,
                             boolean isEnd) {
        if (vadEnabled) {
            vad.inputWave(voiceData, offset, length, isEnd);

            if (vad.getVoiceStartFrame() < 0) {
                int recTime = (int) (framNum / 16 / 2);

                if (recTime > MAXMUTETIME) {
                    LogUtil.d(TAG, "VAD_NOVOICE");
                    vadStart = true;
                    vadStop();
                    Message msg = mainHandler.obtainMessage(VAD_NOVOICE);
                    mainHandler.sendMessage(msg);
                    framNum = 0;
                }
                return;
            }
            voiceData = vad.getVoice(vad.getVoiceStartFrame()
                    + vadVoiceFrameNum, vad.getVoiceFrameNum()
                    - vadVoiceFrameNum);
            if (vadVoiceFrameNum == 0 && voiceData != null) {
                LogUtil.d(TAG, "VAD_START");
                vadStart = true;
                Message msg = mainHandler.obtainMessage(VAD_START);
                msg.arg1 = vad.getVoiceStartFrame();
                mainHandler.sendMessage(msg);
            }
            vadVoiceFrameNum = vad.getVoiceFrameNum();
            offset = 0;
            if (voiceData != null) {
                length = voiceData.length;
                Message msg = mainHandler.obtainMessage(VAD_DATA);
                msg.obj = voiceData;
                msg.arg1 = length;

                mainHandler.sendMessage(msg);
            } else
                length = 0;
            if (vad.getVoiceStopFrame() >= 0) {
                isEnd = true;
                LogUtil.d(TAG, "VAD_STOP");
                vadStop();
                Message msg = mainHandler.obtainMessage(VAD_STOP);
                msg.arg1 = vad.getVoiceStopFrame();
                mainHandler.sendMessage(msg);
            }
        }
    }

    private Runnable procVoiceRun = new Runnable() {

        @Override
        public void run() {
            boolean isLastData = false;
            while (true) {
                while (!waveQueue.isEmpty()) {
                    byte[] queueData = waveQueue.poll();
                    if (endQueue && waveQueue.isEmpty())
                        isLastData = true;
                    setPostData(queueData, 0, queueData.length, isLastData);
                    if (endQueue)
                        break;
                }
                if (endQueue) {
                    vad.reset();
                    if (!vadStart) {
                        Message msg = mainHandler.obtainMessage(VAD_NOVOICE);
                        mainHandler.sendMessage(msg);
                    }
                    vadStart = false;
                    break;
                }
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    public void setMaxMuteTime(int mTime) {
        MAXMUTETIME = mTime;
    }

    public void vadStart() {
        if (isWorking)
            return;
        LogUtil.d(TAG, "vadStart()");
        framNum = 0;
        vadVoiceFrameNum = 0;
        waveQueue.clear();
        endQueue = false;
        isWorking = true;
        vadStart = false;
        // vad.reset();
        new Thread(procVoiceRun).start();
    }

    public void vadStop() {
        LogUtil.d(TAG, "vadStop()");
        endQueue = true;
        isWorking = false;
        waveQueue.clear();
    }

    public boolean isVadEnabled() {
        return vadEnabled;
    }

    public boolean isWorking() {
        return isWorking;
    }

    public void setVadEnabled(boolean vadEnabled) {
        if (isWorking)
            return;
        this.vadEnabled = vadEnabled;
    }
}
